package cn.icanci.loopstack.utils.diff;

import java.lang.reflect.Field;
import java.util.*;

import org.apache.commons.lang3.StringUtils;

import com.google.common.collect.Sets;

/**
 * TODO 存在层级对象，需要设计递归实现
 * 
 * @author icanci
 * @since 1.0 Created in 2023/01/07 23:50
 */
public class ObjectDiffUtils {
    private ObjectDiffUtils() {
    }

    private static final String COMMA = "，";

    /**
     * 属性比较
     *
     * @param source 源数据对象
     * @param target 目标数据对象
     * @return 对应属性值的比较变化
     */
    public static String compare(Object source, Object target) {
        if (Objects.isNull(source) && Objects.isNull(target)) {
            return StringUtils.EMPTY;
        }
        if (source.getClass() != target.getClass()) {
            return StringUtils.EMPTY;
        }
        Set<String> ignoreCompareFields = Sets.newHashSet();
        Map<String, CompareNode> sourceMap = getFiledValueMap(source, ignoreCompareFields);
        Map<String, CompareNode> targetMap = getFiledValueMap(target, ignoreCompareFields);
        if (sourceMap.isEmpty() && targetMap.isEmpty()) {
            return StringUtils.EMPTY;
        }
        // 如果源数据为空，则只显示目标数据，不显示属性变化情况
        if (sourceMap.isEmpty()) {
            return doEmpty(targetMap, ignoreCompareFields);
        }
        // 如果源数据为空，则显示属性变化情况
        String s = doCompare(sourceMap, targetMap, ignoreCompareFields);
        if (!s.endsWith(COMMA)) {
            return s;
        }
        return s.substring(0, s.length() - 1);
    }

    private static String doEmpty(Map<String, CompareNode> targetMap, Set<String> ignoreCompareFields) {
        StringBuilder sb = new StringBuilder();
        Collection<CompareNode> values = targetMap.values();
        int size = values.size();
        int current = 0;
        for (CompareNode node : values) {
            current++;
            Object o = Optional.ofNullable(node.getFieldValue()).orElse(StringUtils.EMPTY);
            if (Objects.nonNull(ignoreCompareFields) && ignoreCompareFields.contains(node.getFieldKey())) {
                continue;
            }
            if (o.toString().length() > 0) {
                sb.append("[").append(node.getFieldName()).append("：").append(o).append("]");
                if (current < size) {
                    sb.append(COMMA);
                }
            }
        }
        return sb.toString();
    }

    private static String doCompare(Map<String, CompareNode> sourceMap, Map<String, CompareNode> targetMap, Set<String> ignoreCompareFields) {
        StringBuilder sb = new StringBuilder();
        Set<String> keys = sourceMap.keySet();
        int size = keys.size();
        int current = 0;
        for (String key : keys) {
            current++;
            CompareNode sn = sourceMap.get(key);
            CompareNode tn = targetMap.get(key);
            if (Objects.nonNull(ignoreCompareFields) && ignoreCompareFields.contains(sn.getFieldKey())) {
                continue;
            }
            String sv = Optional.ofNullable(sn.getFieldValue()).orElse(StringUtils.EMPTY).toString();
            String tv = Optional.ofNullable(tn.getFieldValue()).orElse(StringUtils.EMPTY).toString();
            // 只有两者属性值不一致时, 才显示变化情况
            if (!sv.equals(tv)) {
                sb.append(String.format("[%s：%s -> %s]", sn.getFieldName(), sv, tv));
                if (current < size) {
                    sb.append(COMMA);
                }
            }
        }
        return sb.toString();
    }

    private static Map<String, CompareNode> getFiledValueMap(Object t, Set<String> ignoreCompareFields) {
        if (Objects.isNull(t)) {
            return Collections.emptyMap();
        }
        Field[] fields = t.getClass().getDeclaredFields();
        if (fields.length == 0) {
            return Collections.emptyMap();
        }
        Map<String, CompareNode> map = new LinkedHashMap<>();
        for (Field field : fields) {
            Diff diffAnnotation = field.getAnnotation(Diff.class);
            String fieldName = field.getName();
            if (!Objects.isNull(diffAnnotation)) {

                if (diffAnnotation.ignore()) {
                    ignoreCompareFields.add(field.getName());
                }
                if (StringUtils.isNotBlank(diffAnnotation.fieldName())) {
                    fieldName = diffAnnotation.fieldName();
                }
            }
            field.setAccessible(true);
            try {
                String fieldKey = field.getName();
                CompareNode node = new CompareNode();
                node.setFieldKey(fieldKey);
                node.setFieldValue(field.get(t));
                node.setFieldName(fieldName);
                map.put(field.getName(), node);
            } catch (IllegalArgumentException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return map;
    }
}
